#ifndef RENDERER_H
#define RENDERER_H

#include "Resource.h"
#include "CommonTypes.h"
#include "MemUtils.h"

#include "EffectManager.h"
#include "GpuBufferManager.h"

namespace Cvekas {

class Renderable;

/// Describes single GPU work "packet"
struct Batch
{
	/// Default constructor
	Batch();

	/// Copy constructor
	Batch(const Batch& batch);

	/// Assignment operator
	const Batch& operator=(const Batch& batch);

	/// Sort operator.
	/// Batches are sorted first by priority, then by effect ID, pass, vertex buffer ID and index buffer ID
	/// in that order.
	bool operator<(const Batch& batch) const;
	
	/// Batch priority. 
	/// Batches with higher priority value will always be rendered before the ones with lower priority.
	uint16 priority;

	/// This value will be provided to Renderable::onPreRender.
	/// Acts only as user-data, has no meaning from engine's point of view.
	uint16 batch_id;

	// Maybe use pointers instead of ids?

	/// ResourceId of vertex buffer used in this batch.
	ResourceId vb_id;

	/// ResourceId of index buffer used in this batch.
	/// Value of Resource::INVALID_ID indicates that no index buffer is used.
	ResourceId ib_id;

	/// ResourceId of effect used in this batch.
	ResourceId effect_id;

	/// Index of effect technique
	uint technique;
	
	/// Effect pass number.
	uint pass;

	/// Index of where to begin reading index buffer.
	/// Indicates first vertex in vertex buffer if no index buffer is used.
	uint start_index;

	/// Number of triangles to draw.
	uint tri_count;

	/// Renderable which owns this batch.
	/// Can be NULL, but in that case Renderable::onPreRender will not be called before rendering this batch.
	Renderable* owner;
};

/// Base class for objects which can be rendered by GPU
class Renderable
{
public:

	/// Called just before rendering batch. Useful to set effect states.
	/// \param batch_id ID number of batch
	virtual void onPreRender(uint16 batch_id) = 0;

	/// Called by Renderer::prepare function.
	/// \param batches pointer to array of batches
	/// \returns number of batches
	virtual uint getBatches(Batch** batches) = 0;
};

/// Renderer statistics of a single frame
struct FrameStats
{
	uint triangle_count;
	uint batch_count;
	uint effect_changes;
	uint pass_changes;
	uint ib_changes;
	uint vb_changes;
};

/// Object responsible for sending Renderables and Batches to GPU in most optimal way
class Renderer : public boost::noncopyable
{
public:

	/// Constructor.
	Renderer(D3DDevicePtr device, EffectManagerPtr effect_manager, GpuBufferManagerPtr gpu_buffer_manager);

	/// Destructor.
	~Renderer();

	/// Registers Renderable for getting rendered at next frame.
	void prepare(Renderable& renderable);

	/// Registers Batch for getting rendered at next frame.
	void prepare(Batch& batch);

	/// Renders all registered Renderables.
	void render(uint16 min_priority = MAX_UINT16, uint16 max_priority = MAX_UINT16);
	
	/// Clears internal data structures and updates stats. Must be called at the end of each frame.
	void endFrame();

	/// Provides last frame statistics.
	const FrameStats& getStatistics();

private:
	void sort();
	void reset();

	// Maximum amount of batches per frame renderer can draw
	static const uint max_batches = 1024;
	
	D3DDevicePtr device;
	
	boost::array<Batch, max_batches> batches;
	uint batch_count;
	bool needs_reset;
	bool is_sorted;

	ResourceId active_vb;
	uint active_vb_start_index;
	ResourceId active_ib;
	ResourceId active_effect;
	uint active_technique;
	uint active_pass;

	FrameStats stats;
	FrameStats last_stats;

	// Managers
	EffectManagerPtr effect_manager;
	GpuBufferManagerPtr gpu_buffer_manager;
};

typedef boost::shared_ptr<Renderer> RendererPtr;

} // namespace

#endif